/*
 * REFIOEventDispatcher.h
 *
 *  Created on: Jun 5, 2013
 *      Author: Lucifer
 */

#ifndef __BFX_REFTASKIOEVENTDISPATCHER_H__
#define __BFX_REFTASKIOEVENTDISPATCHER_H__

#include <stddef.h>
#include "SelectInterrupter.h"
#include "REFIOEventService.h"

namespace BFX
{

#ifndef	BFX_IO_HAS_IOCP

class REFIOEventService;

////////////////////////////////////////////////////////////////////////////////

/**
 * This class represents a operations dispatch queue.
 */
class IOEventDispatchQueue
{
public:
	/**
	 * Initializes a new instance of BFX::SAEventDispatchQueue.
	 */
	IOEventDispatchQueue();
	/**
	 * Destroys the BFX::SAEventDispatchQueue instance.
	 */
	virtual ~IOEventDispatchQueue();

	/**
	 * Add a new operation to the queue.
	 *
	 * @param fd The file descriptor.
	 * @param pContext The operation associated with the given file descriptor.
	 * @return Returns true if this is the only operation for the given file descriptor. In this case the event
	 * 		demultiplexing function my need to restarted.
	 */
	bool WaitEvent(SOCKET fd, REFIOEventContext* pContext);

	/**
	 * Cancel all contexts associated with the descriptor. Any operations pending for file descriptor will be
	 * notified that they have been canceled.
	 *
	 * @param[in] fd The file descriptor.
	 * @param[out] pContexts The queue uses to receive canceled event contexts.
	 * @param[in] nErrorCode The system error code on this cancel operation.
	 * @return True if any operations were canceled. In this case the event demultiplexing function may need to restarted.
	 */
	bool StopWaitEvents(SOCKET fd, REFIOEventContextQueue* pContexts, int nErrorCode);

	/**
	 * Fill a descriptor set with the descriptors corresponding to each active operations.
	 *
	 * @param pFdSet[in,out] The file descriptor set.
	 * @param pContexts[out] The queue stores contexts only when descriptors fail to be added to the descriptor set.
	 */
	void GetFileDescriptors(REFFileDescriptorSet* pFdSet, REFIOEventContextQueue* pContexts);

	/**
	 * Perform the operations corresponding to the descriptor.
	 *
	 * @param fd[in] The file descriptor.
	 * @param pOperations[out] The queue to receive operations will be completed.
	 * @return True if there are still unfinished operations queued for the descriptor.
	 */
	bool PerformEvents(SOCKET fd, REFIOEventContextQueue* pOperations);

	/**
	 * Perform the operations corresponding to the ready file descriptors contained in the given descriptor set.
	 *
	 * @param pFdSet[in] The file descriptor set.
	 * @param pContexts[out] The queue to receive contexts will be completed.
	 */
	void PerformEvents(REFFileDescriptorSet* pFdSet, REFIOEventContextQueue* pContexts);

	/**
	 * Gets a value indicating whether the queue is empty.
	 *
	 * @return True if empty; otherwise false.
	 */
	bool IsEmpty() const;

	/**
	 * Clear the queue.
	 * @param pContexts[out] The queue to receive contexts will be completed.
	 */
	void Clear(REFIOEventContextQueue* pContexts = NULL);

private:
	// Forbidden copy constructor and assignment operator.
	IOEventDispatchQueue(const IOEventDispatchQueue&);
	IOEventDispatchQueue& operator = (const IOEventDispatchQueue&);

private:
	typedef	Queue<REF<REFIOEventContext> >	ContextQueue;
	typedef	HashMap<SOCKET, ContextQueue>	ContextMap;

	ContextMap	m_contexts;
};

////////////////////////////////////////////////////////////////////////////////
// REFIOEventDispatcher

class REFIOEventDispatcher;

#if defined(BFX_IO_HAS_EPOLL)

#elif defined(BFX_IO_HAS_KQUEUE)

#elif defined(BFX_IO_HAS_DEV_POLL)

#else	//	BFX_IO_HAS_SELECT

/**
 * This class represents a reactor initiation-dispatcher. Internally it is uses select() for demultiplexing.
 */
class REFIOEventDispatcher : public REFObject
{
public:
	/**
	 * Types of events handled by the dispatcher
	 */
	enum EVENT_TYPE
	{
		READ_EVENT			= 0,
		WRITE_EVENT			= 1,
		EXCEPT_EVENT		= 2,
		MAX_SELECT_EVENTS	= 3,
#if defined(_WIN32) || defined(__CYGWIN__)
		CONNECT_EVENT		= 3,
		MAX_EVENTS			= 4,
#else
		CONNECT_EVENT		= 1,
		MAX_EVENTS			= 3,
#endif
	};

public:
	/**
	 * Initializes a new instance of BFX::REFSelectDispatcher.
	 * @param[in] pIOService the own IO event service.
	 */
	REFIOEventDispatcher(REFIOEventService* pIOService);

	/**
	 * Destroys the BFX::REFSelectDispatcher instance.
	 */
	virtual ~REFIOEventDispatcher();

	/**
	 * Register the file descriptor with the dispatcher.
	 *
	 * @param fd The file descriptor.
	 * @return True on success; otherwise false.
	 */
	bool RegisterFileDescriptor(SOCKET fd);

	/**
	 * Unregister the file descriptor.
	 * Cancel all operations associated with the given descriptor.
	 *
	 * @param fd The file descriptor.
	 */
	void UnregisterFileDescriptor(SOCKET fd);

	/**
	 * Starts a new operation.
	 * The operation will be performed when the given descriptor is flagged as ready, or an error has occurred.
	 *
	 * @param fd The file descriptor.
	 * @param pContext The event context associated to the given descriptor.
	 * @param event The event type.
	 */
	void WaitEvent(SOCKET fd, REFIOEventContext* pContext, EVENT_TYPE event);

	/**
	 * Run select once until interrupted or events are ready to be dispatched.
	 *
	 * @param[in] bBlocking The value that indicates whether the event-loop is in blocking mode.
	 * @param[out] pOperations The queue to receive operations will be completed.
	 */
	void DispatchEvents(bool bBlocking, REFIOEventContextQueue* pOperations);

	/**
	 * Interrupt the select loop.
	 */
	void Interrupt()
	{
		m_interrupter.Interrupt();
	}

	/**
	 * Shutdown the dispatcher.
	 * Destroys all user defined objects.
	 */
	void Shutdown();

private:
	/**
	 * Cancel all operations associated with the given descriptor. The event operations associated with the file
	 * descriptor will be invoked with given error code.
	 *
	 * @param[in] fd The file descriptor.
	 * @param[in] nErrorCode The system error code on this cancel operation. The default specifying value (0) indicates
	 * 		canceled by user aborted.
	 */
	void StopWaitEvents(SOCKET fd, int nErrorCode = 0);

private:
	REFIOEventService*	m_pIOService;

	bool	m_bShutdown;
	Mutex	m_mutex;									// The mutex is uses to protect access to internal data.
	SelectInterrupter	m_interrupter;					// The interrupter is uses to break a blocking select call.

	IOEventDispatchQueue	m_queue[MAX_EVENTS];		// The queues of read, write and except operations.
};

#endif	// !BFX_IO_HAS_SELECT

#endif	//	!BFX_IO_HAS_IOCP

} /* namespace BFX */

#endif /* __BFX_REFTASKIOEVENTDISPATCHER_H__ */
